/**
 * 1. 折叠功能 （完成）
 * 2. 右键菜单功能 (完成)
 * 3. 自定义事件 (完成)
 * 4. 不同层级不同颜色还有大小 (完成)
 * 5. 修改标签整个字体样式
 * 6. 修改标签内单个文字不同样式
 * 7. 支持插入图片
 * 8. 拖拽排序 （完成）
 * 9. 框选多选 （完成）
 */

import { fabric } from 'fabric'
import { Canvas } from 'fabric/fabric-impl'
// import { BasicItem } from './BasicItem'
import { ChildItem } from './ChildItem'
import { ChildItemOption, ItemStyle, ICanvas, hasParentType, ExportJsonItem } from './types/global'
import { DragSort } from './DragSort'
import { EventEmitter } from './EventEmitter'
import { ContextMenu } from './ContextMenu'
import { Select } from './Select'

export default class DaoDaoMindMap extends EventEmitter{
	wrapperEl:  HTMLElement | null
	canvasEl: HTMLCanvasElement | null
	canvas: ICanvas | null
	width: number
	height: number
	margin: number
	dragFlag: boolean
	originPointer: fabric.Point
	mapData: ChildItem[]
	frontLineLength: number
	afterLineLength: number
	sortFlag: boolean
	dragSort: DragSort | null
	contextMenu: ContextMenu | null
	styleList: ItemStyle[]
	select: Select | null
	constructor(el: string | HTMLElement) {
		super()
		this.wrapperEl = null
		this.canvasEl = null
		this.canvas = null
		this.width = 600
		this.height = 300
		this.margin = 12
		this.dragFlag = false
		this.originPointer = new fabric.Point(0,0)
		this.mapData = []
		this.frontLineLength = 50
		this.afterLineLength = 10
		this.sortFlag = false
		this.dragSort = null
		this.contextMenu = null
		this.select = null
		this.styleList = [
			{
				color: '#fff',
				backgroundColor: '#1cabfc',
				padding: 20,
				textBackgroundColor: 'transparent',
				textDecoration: 'none',
				lineHeight: 1.2,
				fontSize: 24
			},
			{
				color: '#000',
				backgroundColor: '#fdd564',
				padding: 16,
				textBackgroundColor: 'transparent',
				textDecoration: 'none',
				lineHeight: 1.2,
				fontSize: 16
			},
			{
				color: '#000',
				backgroundColor: 'transparent',
				padding: 12,
				textBackgroundColor: 'transparent',
				textDecoration: 'none',
				lineHeight: 1.2,
				fontSize: 14
			}
		]
		this.init(el)
	}
	init(el: string | HTMLElement) {
		// 初始化 this.wrapperEl
		let wrapper = null
		// 如果节点是一个元素节点，nodeType 属性返回 1
		if(typeof el === 'object' && el.nodeType === 1) {
			wrapper = el
		} else if(typeof el === 'string') {
			const wrapperEl = document.getElementById(el)
			if(wrapperEl){
				wrapper = wrapperEl
			}else{
				wrapper = null
			}
		} else {
			wrapper = null
		}
		if(!wrapper) {
			throw Error('DaoDaoMindMap 需要传入一个有效的id或HTMLElement (DaoDaoMindMap needs to be passed in a valid id or HTMLElement)')
		}
		//元素必须是relateive定位
		const cssStyle = getComputedStyle(wrapper)
		const isAbsolute = cssStyle.position === 'absolute'
		if(!isAbsolute) {
			wrapper.style.position = 'relative'
		}

		this.wrapperEl = wrapper
		this.canvasEl = document.createElement('canvas')
		this.canvasEl.id = 'ddmp'
		this.wrapperEl.innerHTML = ''
		this.wrapperEl.appendChild(this.canvasEl)
		this.canvas = new fabric.Canvas('ddmp',{
			selection: false,
			fireRightClick: true
		})
		// 初始化width 和 height
		this.width = this.wrapperEl.clientWidth
		this.height = this.wrapperEl.clientHeight
		this.canvas.setDimensions({
			width: this.width,
			height: this.height
		})
		this.dragSort = new DragSort(this)
		this.contextMenu = new ContextMenu(this)
		this.select = new Select(this)
		this.canvas.on('mouse:dblclick', (e) => {
			if(e.target === null) {
				this.addItem({
					left: e.absolutePointer?.x,
					top: e.absolutePointer?.y
				})
				this.select?.activeItem?.expandButton.update()
			}
		})
		this.canvas.on('mouse:wheel', (e) => {
			e.e.preventDefault()
			if(e.pointer && e.e.deltaY) {
				let zoom = (this.canvas as Canvas).getZoom() - e.e.deltaY / 1000
				if(zoom <= 0.1){
					zoom = 0.1
				}
				this.canvas?.zoomToPoint(e.pointer, zoom)
				this.select?.activeItem && this.select.activeItem?.redrawBox()
			}
		})
		this.canvas.on('mouse:down',(e) => {
			this.originPointer = e.pointer as fabric.Point
			if(e.button === 1){
				if(e.target === null) {
					this.dragFlag = true
				}else{
					const target = e.target as hasParentType<fabric.Object | undefined>
					if(target.parent) {
						this.sortFlag = true
						this.dragSort && this.dragSort.update(e)
					}else{
						this.dragFlag = true
					}
				}
			}
			if(e.button === 3) {
				this.contextMenu?.hideMenu()
			}
		})
		this.canvas.on('mouse:move',(e) => {
			if(e.target?.type === 'textbox' && (e.target as fabric.Textbox)?.isEditing) {
				return 
			}
			if(this.dragFlag) {
				const currentPointer = e.pointer as fabric.Point
				const zoom = this.canvas?.getZoom() || 1
				const deltaX = currentPointer.x - this.originPointer.x
				const deltaY = currentPointer.y - this.originPointer.y
				const newPointer = new fabric.Point(this.originPointer.x + deltaX, this.originPointer.y + deltaY)
				const originMatrix = this.canvas?.viewportTransform || [0,0,0,0,0,0]
				this.canvas!.setViewportTransform([zoom,0,0,zoom,originMatrix[4] + deltaX,originMatrix[5] + deltaY])
				this.originPointer = newPointer
			}
			if(this.sortFlag) {
				const currentPointer = e.pointer as fabric.Point
				const deltaX = Math.abs(currentPointer.x - this.originPointer.x)
				const deltaY = Math.abs(currentPointer.y - this.originPointer.y)
				if(deltaX > 4 || deltaY > 4){
					if(this.select?.activeItem?.tempChildrenVisible && this.select.activeItem.children.length > 0) {
						this.select.activeItem.tempChildrenVisible = false
						this.select.activeItem.hide()
					}
				}
				this.dragSort && this.dragSort.update(e)
			}
		})
		this.canvas.on('mouse:up',(e) => {
			if(this.sortFlag && this.select?.activeItem) {
				if(!this.select.activeItem?.tempChildrenVisible) {
					this.select.activeItem.tempChildrenVisible = true
					this.select.activeItem.show()
				}
				this.dragSort && this.dragSort.save()
			}
			this.sortFlag = false
			this.dragFlag = false

			const currentPointer = e.pointer as fabric.Point
			const deltaX = Math.abs(currentPointer.x - this.originPointer.x)
			const deltaY = Math.abs(currentPointer.y - this.originPointer.y)
			if(deltaX < 4 && deltaY < 4){
				// 打开菜单
				if(e.button === 3) {
					this.contextMenu?.showMenu(e)
				}
				if(e.button === 1) {
					this.contextMenu?.hideMenu()
				}
			}

			this._refresh()
		})

		this.canvas.contextTop?.canvas.addEventListener('contextmenu',(e: MouseEvent) => {
			e.preventDefault()
		})
		
		document.addEventListener('keydown', (e) => {
			if(this.select?.activeItems && this.select?.activeItems.length > 0) {
				switch(e.key) {
				case 'Tab':
					e.preventDefault()
					if(this.select.activeItem && this.select.activeItem.childrenVisible) {
						this.addItem({
							left: this.select.activeItem.iText.left,
							top: this.select.activeItem.iText.top,
							parent: this.select.activeItem
						})
					}
					break
				case 'Delete':
					this.deleteItem()
				}
			}
		})
	}
	addItem(option: ChildItemOption) {
		const item = new ChildItem(this, {text:'节点'+this.mapData.length,...option})
		this.mapData.push(item)
		//if(option.parent) {
		this.countMapPosition()
		//}
		this._refresh()

		return item
	}
	countMapPosition() {
		// 将所有节点需要计算的属性清空
		for(let i = 0;i < this.mapData.length; i++) {
			this.mapData[i].totalchildHeight = 0
			this.mapData[i].totalchildNum = 0
			this.mapData[i].level = 0
		}
		// 计算节点层级
		for(let i = 0;i < this.mapData.length; i++) {
			let current = this.mapData[i]
			while(current.parent !== null) {
				this.mapData[i].level += 1
				current = current.parent
				current.totalchildNum += 1
			}
		}
		// 计算每个节点的占用高度
		for(let i = 0;i < this.mapData.length; i++) {
			let current = this.mapData[i]
			const styleLevel = current.level >= this.styleList.length ? this.styleList.length - 1 : current.level
			current.setStyle(this.styleList[styleLevel], false)
			if(current.visible) {
				const height = current.children.length > 0 && current.childrenVisible ? 0 : (current.getHeight() + this.margin * 2)
				if(current.children.length === 0 || !current.childrenVisible) {
					current.totalchildHeight = height
				}
				while(current.parent !== null) {
					current = current.parent
					current.totalchildHeight += height
				}
			}
		}
		// 渲染元素位置
		const firstlevel = this.mapData.filter(item => !item.parent)
		firstlevel.forEach(item => {
			this.countItemPosition(item, 0)
		})
		this.countBoundingBox()
	}
	countItemPosition(item: ChildItem, index:number) {
		// 如果item没有父元素就按照给定的位置渲染,因此只需要计算item.parent不为null的
		if(item.parent) {
			const xPos = item.parent.getWidth() + this.frontLineLength + this.afterLineLength
			let yPos = 0
			const parent = item.parent
			const brotherList = parent.children
			// 计算当前节点的兄弟节点总高度
			let bortherListHeight = 0
			brotherList.forEach((brotherItem) => {
				bortherListHeight += (brotherItem.totalchildHeight > (brotherItem.getHeight()+this.margin * 2) ? brotherItem.totalchildHeight : brotherItem.getHeight() + this.margin * 2)
			})
			// 根据index计算当前节点的位置
			for(let i = 0;i<index;i++) {
				yPos += (brotherList[i].totalchildHeight > (brotherList[i].getHeight()+this.margin * 2) ? brotherList[i].totalchildHeight : brotherList[i].getHeight() + this.margin * 2)
			}
			// 往上偏移，达到居中效果
			yPos -= bortherListHeight / 2
			yPos += (parent.getHeight() + this.margin * 2) / 2
			yPos -= (parent.padding - item.padding)
			if(item.totalchildHeight > (item.getHeight() + this.margin * 2)) {
				yPos += item.totalchildHeight / 2
				yPos -= (item.getHeight() + this.margin * 2) / 2
			}
			
			item.changePosition({
				x:xPos,
				y:yPos
			})
		}
		item.updateAfterLine()
		for(let i = 0;i<item.children.length;i++) {
			this.countItemPosition(item.children[i], i)
		}
	}
	countBoundingBox() {
		// 重置boundingbox
		for(let i = 0;i < this.mapData.length; i++) {
			const item = this.mapData[i]
			const top = (item.rect.top || 0)// - this.margin
			const bottom = (item.rect.top || 0) + item.getHeight()// + this.margin
			const left = (item.rect.left || 0)
			const right = left + item.getWidth() + (item.children.length === 0 || !item.childrenVisible ? this.afterLineLength + this.frontLineLength + 50 : 0)
			this.mapData[i].boundingBoxWidthChild = {
				top: top,
				bottom: bottom,
				left: left,
				right: right
			}
		}
		// 计算每个节点的boundingbox
		for(let i = 0;i < this.mapData.length; i++) {
			let current = this.mapData[i]
			const item = this.mapData[i]
			if(current.visible) {
				while(current.parent !== null) {
					current = current.parent
					if(current.boundingBoxWidthChild.top > item.boundingBoxWidthChild.top) {
						current.boundingBoxWidthChild.top = item.boundingBoxWidthChild.top
					}
					if(current.boundingBoxWidthChild.bottom < item.boundingBoxWidthChild.bottom) {
						current.boundingBoxWidthChild.bottom = item.boundingBoxWidthChild.bottom
					}
					if(current.boundingBoxWidthChild.left > item.boundingBoxWidthChild.left) {
						current.boundingBoxWidthChild.left = item.boundingBoxWidthChild.left
					}
					if(current.boundingBoxWidthChild.right < item.boundingBoxWidthChild.right) {
						current.boundingBoxWidthChild.right = item.boundingBoxWidthChild.right
					}
				}
			}
		}
		for(let i = 0;i < this.mapData.length; i++) {
			const item = this.mapData[i]
			if(item.parent) {
				//item.boundingBoxWidthChild.left += 20
			}else{
				item.boundingBoxWidthChild.top -= 30
				item.boundingBoxWidthChild.bottom += 30
			}
			item.bounding.setOptions({
				left: item.boundingBoxWidthChild.left,
				top: item.boundingBoxWidthChild.top,
				width: item.boundingBoxWidthChild.right - item.boundingBoxWidthChild.left,
				height: item.boundingBoxWidthChild.bottom - item.boundingBoxWidthChild.top,
				visible: item.visible
			})
		}
	}
	addFreeItem(event: fabric.IEvent<MouseEvent>) {
		this.addItem({
			left: event.pointer?.x || 0,
			top: event.pointer?.y || 0
		})
	}
	addChildItem() {
		this.addItem({
			parent: this.select?.activeItem
		})
	}
	addParentItem() {
		if(this.select?.activeItem) {
			const parent = this.select.activeItem?.parent
			let item = null
			if(parent) {
				item = new ChildItem(this, {parent: parent, text:'节点'+this.mapData.length})
				const index = parent.children.findIndex(item => item === this.select?.activeItem)
				if(index > -1) {
					parent.children.splice(index, 1)
				}
				parent.children.splice(index, 0, item)
				parent.children.pop()
			}else{
				item = new ChildItem(this, {left: this.select.activeItem.iText.left, top:this.select.activeItem.iText.top, text:'节点'+this.mapData.length})
			}
			this.mapData.push(item)
			this.select.activeItem.parent = item
			item.children.push(this.select.activeItem)
			this.countMapPosition()
			this._refresh()
		}
	}
	addSiblingItem() {
		if(this.select?.activeItem) {
			this.addItem({
				parent: this.select.activeItem.parent
			})
		}
	}
	addIcon(path: string, clickFn?: (event:{target: ChildItem}) => void, hoverFn?: (event:{target: ChildItem}) => void) {
		if(this.select?.activeItem) {
			this.select.activeItem.addIcon(path, clickFn, hoverFn)
		}
	}
	deleteItem() {
		if(this.select?.activeItems && this.select.activeItems.length > 0) {
			this.select?.activeItems.forEach(item => {
				item.remove()
			})
			this.countMapPosition()
		}
	}
	addImage(e:Event) {
		if(e) {
			const files = (e.target as HTMLInputElement)?.files
			const file = files && files.length > 0 ? files[0] : null
			if(this.select?.activeItem) {
				this.select?.activeItem.addImage(file)
			}
		}
	}
	deleteImgItem() {
		if(this.select?.activeItem) {
			this.select?.activeItem.deleteImage()
		}
	}
	changeItemStyle(option: ItemStyle) {
		if(this.select?.activeItems && this.select.activeItems.length > 0) {
			this.select?.activeItems.forEach(item => {
				item.setStyle(option)
			})
		}
	}
	changeItemTextStyle(option: ItemStyle) {
		if(this.select?.activeItem) {
			this.select?.activeItem.setTextStyle(option)
		}
	}
	getJsonData() {
		const jsonData:ExportJsonItem[] = []
		const getItemData = (item: ChildItem, parent: ExportJsonItem | null) => {
			const data: ExportJsonItem = {
				text: item.iText.text,
				style: item.staticStyle,
				img: {
					src: item.img?.getSrc(),
					width: item.img?.getScaledWidth(),
					height: item.img?.getScaledHeight()
				},
				children:[]
			}
			if(item.hasChangeWidth) {
				data.width = item.iText.width
			}
			if(!parent) {
				data.left = item.iText.left
				data.top = item.iText.top
				jsonData.push(data)
			}else{
				parent.children && parent.children.push(data)
			}
			if(item.children.length > 0) {
				for(let i = 0;i<item.children.length;i++) {
					getItemData(item.children[i], data)
				}
			}
		}
		// 先找到孤立没有父元素的节点
		const firstlevel = this.mapData.filter(item => !item.parent)
		for(let i = 0;i<firstlevel.length;i++) {
			getItemData(firstlevel[i], null)
		}
		console.log(jsonData)
		return jsonData
	}
	setByJson(jsonData: ExportJsonItem | ExportJsonItem[]) {
		let data:ExportJsonItem[] = []
		if(jsonData instanceof Object && !(jsonData instanceof Array)) {
			data = [jsonData]
		}
		if(jsonData instanceof Array) {
			data = jsonData
		}
		//先清空原本的
		this.mapData.forEach(item => {
			item.remove()
		})
		const setItemByJson = (item: ExportJsonItem & ChildItemOption, parent: ChildItem | null) => {
			if(parent) {
				item.parent = parent
			}
			const parentItem = this.addItem(item)
			if(item.children && item.children.length > 0) {
				for(let i = 0;i<item.children.length;i++) {
					setItemByJson(item.children[i], parentItem)
				}
			}
		}
		//再重新生成
		for(let i = 0;i<data.length;i++) {
			setItemByJson(data[i], null)
		}
	}
	_refresh() {
		setTimeout(() => {
			const zoom = this.canvas?.getZoom() || 1
			const originMatrix = this.canvas?.viewportTransform || [0,0,0,0,0,0]
			this.canvas!.setViewportTransform([zoom,0,0,zoom,originMatrix[4],originMatrix[5]])
		},100)
	}
}